package gicom.config;

import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.StringTokenizer;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.InvalidName;
import org.omg.CosNaming.NamingContextPackage.NotFound;

public class GenericParameter {

    private String clazz;
    private Object[] values;

    public GenericParameter(String clazz, Object[] values) {
        this.clazz = clazz;
        this.values = values;
    }

    public String getClazz() {
        return clazz;
    }

    public Object[] getValues() {
        return values;
    }

    public Object resolv(NamingContextExt nc) throws ClassNotFoundException, NoSuchMethodException, InvalidName, NotFound, CannotProceed, IllegalAccessException, InvocationTargetException {
        return Class.forName(clazz).getMethod("narrow", org.omg.CORBA.Object.class).invoke(null, nc.resolve(nc.to_name((String) values[0])));
    }

    public Object instanciate(NamingContextExt nc) throws InstantiationException, IllegalAccessException, InvocationTargetException {
        return instanciate(nc, values.clone(), 0);
    }

    private Object instanciate(NamingContextExt nc, Object[] values, int index) throws InstantiationException, IllegalAccessException, InvocationTargetException {
        Object foo1 = null;
        Object foo2 = null;

        if (index == values.length) {
            Class[] types = new Class[values.length];
            for (int i = 0; i < types.length; i++) {
                types[i] = values[i].getClass();
            }

            try {
                foo1 = Class.forName(clazz).getConstructor(types).newInstance(values);
            } catch (Exception ex) {
                try {
                    return resolv(nc);
                } catch (Exception ex2) {
                    throw new InstantiationException("No constructor can suit the request.");
                }
            }
        } else {
            try {
                foo1 = instanciate(nc, values, index + 1);
            } catch (Exception ex) {

            }

            if (values[index].getClass() == GenericParameter.class) {
                Object tmp = values[index];
                try {
                    values[index] = ((GenericParameter) values[index]).instanciate(nc);
                    foo2 = instanciate(nc, values, index + 1);
                } catch (Exception ex) {

                }
                values[index] = tmp;
            }
        }

        if (foo1 != null && foo2 != null) {
            throw new InstantiationException("More than one constructor can suit the request.");
        }
        if (foo1 == null && foo2 == null) {
            throw new InstantiationException("No constructor can suit the request.");
        }
        if (foo1 == null) {
            foo1 = foo2;
        }

        return foo1;
    }

    private static Object[] concat(Object[]... tabs) {
        int length = 0;
        for (int i = 0; i < tabs.length; i++) {
            length += tabs[i].length;
        }
        Object[] tab = new Object[length];

        int current = 0;
        for (int i = 0; i < tabs.length; i++) {
            System.arraycopy(tabs[i], 0, tab, current, tabs[i].length);
            current += tabs[i].length;
        }

        return tab;
    }

    private static Object[] readExpr(StringTokenizer tokens) throws ParseException, ClassNotFoundException {
        if (!tokens.hasMoreTokens()) {
            return new Object[0];
        }
        return concat(readInst(tokens), readExpr(tokens));
    }

    private static Object[] readInst(StringTokenizer tokens) throws ParseException, ClassNotFoundException {
        if (!tokens.hasMoreTokens()) {
            return new Object[0];
        }
        String clazz = tokens.nextToken("(");
        if (!isClass(clazz)) {
            throw new ParseException("Invalid class name", 0);
        }
        String value = readValue(tokens);
        if (!tokens.nextToken(";").equals(";")) {
            throw new ParseException("';' waited", 0);
        }
        if (isTerm(value)) {
            return new Object[]{new GenericParameter(clazz, new Object[]{value.replaceAll("\\(", "(").replaceAll("\\)", ")").replaceAll("\\;", ";")})};
        }
        return new Object[]{new GenericParameter(clazz, readExpr(new StringTokenizer(value, "", true)))};
    }

    private static String readValue(StringTokenizer tokens) throws ParseException {
        if (!tokens.hasMoreTokens() || !tokens.nextToken("(").equals("(")) {
            throw new ParseException("'(' waited", 0);
        }
        int level = 1;
        String value = "";
        while (level > 0) {
            if (!tokens.hasMoreTokens()) {
                break;
            }
            value += tokens.nextToken("()");
            if (!tokens.hasMoreTokens()) {
                break;
            }
            String par = tokens.nextToken("()");
            if (!value.endsWith("\\")) {
                if (par.equals("(")) {
                    level++;
                } else {
                    level--;
                    if (level == 0) {
                        break;
                    }
                }
            }
            value += par;
        }

        if (level > 0) {
            throw new ParseException("')' waited", 0);
        }

        return value;
    }

    private static boolean isClass(String clazz) {
        return clazz.matches("\\p{Alpha}[\\p{Alnum}_\\.]*");
    }

    private static boolean isTerm(String value) {
        if (value.equals("")) {
            return false;
        }
        boolean term = true;
        StringTokenizer tokens = new StringTokenizer(value, "();");
        while (term && tokens.hasMoreTokens()) {
            if (!tokens.nextToken().endsWith("\\") && tokens.hasMoreTokens()) {
                term = false;
            }
        }
        return term;
    }

    public static GenericParameter[] parse(String str) throws ParseException, ClassNotFoundException {
        Object[] temp = readExpr(new StringTokenizer(str, "", true));
        GenericParameter[] parameters = new GenericParameter[temp.length];
        for (int i = 0; i < temp.length; i++) {
            parameters[i] = (GenericParameter) temp[i];
        }
        return parameters;
    }
}
